/*
 * Author: nicolas.bredeche(@)upmc.fr
 * Created on 2005-08-31 -- Refactored on 2014-01-06 
 */

package picoevo.ext;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import picoevo.core.*;
import picoevo.tools.*;

public class StatisticsOperator_Population extends StatisticsOperator {
	
	protected double _bestOfGenerationFitness = 0;
	protected Individual _bestOfGenerationInd = null;
	
	protected double _bestEverFitness = 0;
	protected Individual _bestEverInd = null;
	
	
	public StatisticsOperator_Population() { super("unnamed statistics operator"); }
	public StatisticsOperator_Population(String __name) { super(__name); }

	public void displayStatistics ( Object __o )
	{
		double returnValues[] = computeStatistics( __o );
		
		Display.info ("" + (int)returnValues[1] + " individual(s). Average fitness is " + returnValues[2]);
		Display.info ("Best-of-generation individual is #"+ this._bestOfGenerationInd.getId() +" with fitness " + this._bestOfGenerationFitness + " (position: " + (int)returnValues[3] + ")");
		Display.info ("Best-ever          individual is #"+ this._bestEverInd.getId() + " with fitness " + this._bestEverFitness);
	}
	
	public void logStatistics ( Object __o, String __filename )
	{
	    try {
			BufferedWriter outputFile = new BufferedWriter(new FileWriter(__filename, true));
			double returnValues[] = computeStatistics( __o );
			outputFile.write(""+(int)returnValues[0]+","+(int)returnValues[1]+","+returnValues[2]+","+(int)returnValues[3]+","+returnValues[4]+","+this._bestOfGenerationInd.getId()+","+this._bestOfGenerationFitness+","+","+this._bestEverInd.getId()+","+this._bestEverFitness+"\n");
			outputFile.close();
	    } catch (IOException e) {
	    		Display.error(""+this.getClass().getName()+"::logStatistics(.) - cannot write logfile [" + __filename + "]");
	    }
	}
	
	private double[] computeStatistics(Object __o)
	{
		double returnValues[] = new double [5];
		
		_bestOfGenerationInd = null;
		_bestOfGenerationFitness = 0;
		
		try {
			Population population = (Population)__o;
			
			double bestFitness = 0, avgFitness = 0;
			int bestIndex = 0;
			for ( int i = 0 ; i != population.getSize() ; i++ )
			{
				if (i == 0 )
				{
					bestFitness =  population.getIndividual(0).getFitness();
					bestIndex = 0;
				}
				else
				{
					if ( population.getProperties().getBooleanProperty("OptimisationFlag") == Dictionary.Maximization )
					{
						if ( bestFitness < population.getIndividual(i).getFitness() )
						{
							bestFitness = population.getIndividual(i).getFitness();
							bestIndex = i;
						}
					}
					else
					{
						if ( bestFitness > population.getIndividual(i).getFitness() )
						{
							bestFitness = population.getIndividual(i).getFitness();
							bestIndex = i;
						}
					}
				}

				if ( population.getProperties().getBooleanProperty("OptimisationFlag") == Dictionary.Maximization )
				{
					// if maximization
					if ( bestFitness > this._bestOfGenerationFitness || this._bestOfGenerationInd == null ) // best current generation
					{
						this._bestOfGenerationInd =(Individual)population.getIndividual(i).clone();
						this._bestOfGenerationFitness = bestFitness;
					}
					if ( bestFitness > this._bestEverFitness || this._bestEverInd == null ) // best ever
					{
						this._bestEverInd = (Individual)population.getIndividual(i).clone();
						this._bestEverFitness = bestFitness;
					}
				}
				else
				{
					// if minimization
					if ( bestFitness < this._bestOfGenerationFitness || this._bestOfGenerationInd == null ) // best current generation
					{
						this._bestOfGenerationInd =(Individual)population.getIndividual(i).clone();
						this._bestOfGenerationFitness = bestFitness;
					}
					if ( bestFitness < this._bestEverFitness || this._bestEverInd == null ) // best ever
					{
						this._bestEverInd = (Individual)population.getIndividual(i).clone();
						this._bestEverFitness = bestFitness;
					}	
				}

				
				avgFitness += population.getIndividual(i).getFitness();
			}
			avgFitness /= population.getSize();
			
			// Display statistics
			
			returnValues[0] = population.getGenerationNumber();
			returnValues[1] = (double)population.getSize();
			returnValues[2] = avgFitness;
			returnValues[3] = (double)bestIndex;
			returnValues[4] = bestFitness;
			
			return (returnValues);
			
		}
		catch ( java.lang.ClassCastException e )
		{
			Display.error(""+this.getClass().getName()+"::displayStatistics(.) - Class cast exception");
			return (null);
		}
	}
	
	public void logIndividuals ( Object __o, String __filename )
	{
		try {
			Population population = (Population)__o;
			for ( int i = 0 ; i != population.getSize() ; i++ )
			{
					int generation = population.getGenerationNumber();
					BufferedWriter outputFile = new BufferedWriter(new FileWriter(__filename, true));
					outputFile.write(""+generation+","+i+","+population.getIndividual(i).getFitness()+"\n");
					outputFile.close();
			}
		} catch (IOException e) {
	   		Display.error(""+this.getClass().getName()+"::logIndividuals(.) - cannot write logfile [" + __filename + "]");
		}
	}

	/**
	 * 
	 * @return a clone of the best individual found so far
	 */
	public Individual getBestEver()
	{
		return this._bestEverInd;
	}

	
	/**
	 * 
	 * @return a clone of the best individual found in the (latest-evaluated) current generation
	 */
	public Individual getBestOfGeneration()
	{
		return this._bestOfGenerationInd;
	}
}
